﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Security.Principal;
using System.Runtime.InteropServices;
using System.ComponentModel;
using System.Globalization;
using System.Diagnostics;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;

namespace CSP.Model.Entities
{

    public sealed class ImpersonationUtility
    {

        private static string ADMINDOMAINACCOUNT = @"Contoso\Administrator";//CHANGE THIS!

        private static string ADMINDOMAIN = "CONTOSO";//CHANGE THIS!

        private static string ADMINACCOUNT = "Administrator";//CHANGE THIS!

        private static string ADMINPASSWORD = "pass@word1";//CHANGE THIS!

        private WindowsImpersonationContext _wiContext;

        public IntPtr token;

        /// <summary>

        /// Private ctor.

        /// </summary>

        private ImpersonationUtility()

        { }

        /// <summary>

        /// Start impersonating the administrator.

        /// </summary>

        /// <returns>an ImpersonationUtility instance.</returns>

        public static ImpersonationUtility ImpersonateAdmin()
        {

            ImpersonationUtility imp = new ImpersonationUtility();

            imp._ImpersonateAdmin();

            return imp;

        }

        /// <summary>

        /// Undo the impersonation.

        /// </summary>

        public void Undo()
        {

            if (this._wiContext != null)
            {

                this._wiContext.Undo();

                this._wiContext = null;

            }

        }

        private void _ImpersonateAdmin()
        {

            token = IntPtr.Zero;

            IntPtr tokenDuplicate = IntPtr.Zero;

            try
            {

                // Only start admin impersonation if we're not the admin...

                if (String.Compare(ADMINDOMAINACCOUNT, WindowsIdentity.GetCurrent().Name, true, CultureInfo.InvariantCulture) != 0)
                {

                    // Temporarily stop the impersonation started by Web.Config.

                    // WindowsIdentity.Impersonate() will store the current identity (the

                    // account of the requestor) and return to the account of the ApplicationPool

                    // which is "DOMAIN\SPAdmin".

                    this._wiContext = WindowsIdentity.Impersonate(IntPtr.Zero);

                    // But somehow the reverted account "DOMAIN\SPAdmin" still does

                    // not have enough privileges to access SharePoint objects, so

                    // we're logging in DOMAIN\SPAdmin again...

                    if (NativeMethods.LogonUserA(ADMINACCOUNT, ADMINDOMAIN, ADMINPASSWORD, NativeMethods.LOGON32_LOGON_INTERACTIVE,

                    NativeMethods.LOGON32_PROVIDER_DEFAULT, ref token) != 0)
                    {

                        if (NativeMethods.DuplicateToken(token, 2, ref tokenDuplicate) != 0)
                        {

                            WindowsIdentity wi = new WindowsIdentity(tokenDuplicate);

                            // NOTE: Impersonate may fail if account that tries to impersonate does

                            // not hold the "Impersonate after Authentication" privilege

                            // See local security policy - user rights assignment.

                            // Note that the ImpersonationContext from the Impersonate() call

                            // is ignored. Upon the Undo() call, the original account

                            // will be reinstated.

                            wi.Impersonate();

                        }

                        else
                        {

                            throw new Win32Exception(Marshal.GetLastWin32Error(),

                            "Impersonation: Error duplicating token after logon for user \"DOMAIN\\SPAdmin\"");

                        }

                    }

                    else
                    {

                        throw new Win32Exception(Marshal.GetLastWin32Error(),

                        "Impersonation: Error logging on user \"DOMAIN\\SPAdmin\"");

                    }

                }

            }

            finally
            {

                if (token != IntPtr.Zero)

                    NativeMethods.CloseHandle(token);

                if (tokenDuplicate != IntPtr.Zero)

                    NativeMethods.CloseHandle(tokenDuplicate);

            }

        }

    }

    internal sealed class NativeMethods
    {

        private NativeMethods() { }



        [DllImport("kernel32.dll", CharSet = CharSet.Auto)]

        public static extern bool CloseHandle(IntPtr handle);



        public const int LOGON32_PROVIDER_DEFAULT = 0;

        public const int LOGON32_LOGON_INTERACTIVE = 2;

        public const int LOGON32_LOGON_NETWORK = 3;



        [DllImport("advapi32.dll")]

        public static extern int LogonUserA(String lpszUserName,

        String lpszDomain,

        String lpszPassword,

        int dwLogonType,

        int dwLogonProvider,

        ref IntPtr phToken);



        [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]

        public static extern int DuplicateToken(IntPtr hToken,

        int impersonationLevel,

        ref IntPtr hNewToken);





        [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]

        public static extern bool RevertToSelf();

    }
}

